{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1. numpy.argmax(a, axis=None, out=None)\n",
    "#### 2. torch.cat()\n",
    "#### 3. dropout\n",
    "#### 4. nn.embedding()\n",
    "#### 5. Variable()\n",
    "#### [6. torch.matmul()](#6)\n",
    "#### 7. np.triu()\n",
    "#### 8. softmax()\n",
    "#### [9. logging](#9)\n",
    "#### 10. logging的高级用法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import numpy as np\n",
    "import math\n",
    "import torch.nn as nn\n",
    "# tensor 不能反向传播，但是经过Variable封装过就可以进行反向传播\n",
    "from torch.autograd import Variable\n",
    "from math import exp\n",
    "from matplotlib import pyplot as plt\n",
    "import logging\n",
    "import sys"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1.numpy.argmax(a, axis=None, out=None)\n",
    "## 返回沿轴axis最大值的索引"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a :\n",
      " [[  2   3 124]\n",
      " [  5  13   6]]\n",
      "a.argmax()/np.argmax(a)\t: 2\n",
      "a.argmax(0):\t [1 1 0]\n",
      "np.argmax(a, 1):\t [2 1]\n"
     ]
    }
   ],
   "source": [
    "a = np.array([2,3,124,5,13,6]).reshape(2, 3)\n",
    "print(\"a :\\n\", a)\n",
    "print(\"a.argmax()/np.argmax(a)\\t:\", a.argmax())# 默认情况取出所有元素平铺的最大值\n",
    "print(\"a.argmax(0):\\t\", a.argmax(0))# 取出第0维的最大值\n",
    "print(\"np.argmax(a, 1):\\t\", np.argmax(a, 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2.torch.cat()\n",
    "## 拼接"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.cat((a, b):\n",
      " tensor([[0., 0., 0.],\n",
      "        [0., 0., 0.],\n",
      "        [1., 1., 1.],\n",
      "        [1., 1., 1.]])\n"
     ]
    }
   ],
   "source": [
    "a = torch.zeros(2, 3)\n",
    "b = torch.ones(2, 3)\n",
    "print(\"torch.cat((a, b):\\n\",  torch.cat((a, b)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3.dropout\n",
    "## 按照百分比随机置零，防止过拟合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input:\t tensor([[ 0.7554,  0.6855,  0.5774,  1.2734,  0.1017],\n",
      "        [ 1.2634, -0.5618, -1.3442, -0.0215,  0.3606],\n",
      "        [ 0.3445,  1.4696, -1.1165,  0.8205, -1.6897],\n",
      "        [-1.1459, -1.3654, -0.7132,  0.2346, -0.5151]])\n",
      "====================万能的分隔符====================\n",
      "output:\t tensor([[ 0.9442,  0.8569,  0.7218,  0.0000,  0.1272],\n",
      "        [ 1.5792, -0.7023, -1.6802, -0.0269,  0.4508],\n",
      "        [ 0.4306,  0.0000, -1.3957,  1.0256, -2.1121],\n",
      "        [-1.4323, -1.7068, -0.8916,  0.2932, -0.6439]])\n"
     ]
    }
   ],
   "source": [
    "dropout = nn.Dropout(p=0.2)# 以所有数据的20%置零\n",
    "input = torch.randn(4,5)\n",
    "print(\"input:\\t\",  input)\n",
    "\n",
    "print(20*\"=\" + \"万能的分隔符\"+ 20*\"=\")\n",
    "output = dropout(input)\n",
    "print(\"output:\\t\",  output)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4.nn.embedding()\n",
    "> torch.nn.Embedding(num_embeddings, embedding_dim, padding_idx=None, max_norm=None,  norm_type=2.0, cale_grad_by_freq=False, sparse=False,  _weight=None)\n",
    "* num_embeddings: 词典大小，比如共有5000个词，那么输入就是5000\n",
    "* embedding_dim: 嵌入向量的维度，即用多少维来表示一个符号\n",
    "* padding_idx: 填充id, 比如输入长度为100，但是每个句子的长度不同，后面需要用统一的数字进行填充\n",
    "* max_norm: 最大范数，如果嵌入向量的范数超过了这个界限，就要进行归一化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor_word: tensor([[1, 2, 3],\n",
      "        [0, 3, 4]])\n",
      "embedded: tensor([[[-0.0607, -2.6563, -0.2955,  0.6604],\n",
      "         [-0.3869, -0.3773, -0.5557,  0.1602],\n",
      "         [ 0.3218, -0.2742, -0.6181,  0.8148]],\n",
      "\n",
      "        [[ 0.1191,  0.5122,  0.7239,  2.1365],\n",
      "         [ 0.3218, -0.2742, -0.6181,  0.8148],\n",
      "         [-0.0726, -0.1989,  0.2038,  1.0625]]], grad_fn=<EmbeddingBackward0>)\n",
      "embedded.shape: torch.Size([2, 3, 4])\n"
     ]
    }
   ],
   "source": [
    "# 实例化一个词嵌入实例\n",
    "embedding = nn.Embedding(5, 4)# 语料库的大小：5 词嵌入的维度：4\n",
    "\n",
    "'''每个数字代表一个词，例如 {'!':0,'how':1, 'are':2, 'you':3,  'ok':4}'''\n",
    "'''进入embedding的必须是数字，因此如果是字符，要先编码成对应的数字，数字的范围只能在0～4之间，因为上面定义了语料库的大小：5'''\n",
    "word = [\n",
    "    [1, 2, 3],\n",
    "    [0, 3, 4]\n",
    "    # [4, 5, 6]这么给出一个字符的编码 是不正确的\n",
    "]\n",
    "\n",
    "tensor_word = torch.tensor(word)\n",
    "print(\"tensor_word:\", tensor_word)\n",
    "embedded = embedding(tensor_word)# 传入的数据必须是二维数据，因为实例化的对象就是一个二维,第一维表示一个batch，第二位表示seq_len * d_model\n",
    "print(\"embedded:\", embedded)\n",
    "print(\"embedded.shape:\", embedded.shape)# [batch_size, seq_len, d_model]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5.Variable()\n",
    "\n",
    "1. 在pytorch中的Variable就是一个存放会变化值的地理位置，里面的值会不停发生变化，就像一个装鸡蛋的篮子，鸡蛋数会不断发生变化\n",
    "  * 那谁是里面的鸡蛋呢，自然就是pytorch中的tensor了。\n",
    "2. tensor不能反向传播，  Variable能够反向传播"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1., 2., 3.],\n",
      "        [4., 3., 1.]])\n",
      "tensor([[1., 2., 3.],\n",
      "        [4., 3., 1.]], requires_grad=True)\n",
      "====================万能的分隔符====================\n",
      "经过Variable封装的数据和其余tensor进行运算，得到的结果还是variable形式\n",
      " tensor([[2., 4., 6.],\n",
      "        [8., 6., 2.]], grad_fn=<AddBackward0>)\n",
      "====================万能的分隔符====================\n",
      "要想变成普通的tensor，要用.data方法：\n",
      " tensor([[2., 4., 6.],\n",
      "        [8., 6., 2.]])\n"
     ]
    }
   ],
   "source": [
    "'''Only Tensors of floating point and complex dtype can require gradients'''\n",
    "\n",
    "# 梯度更新的tensor必须是float类型\n",
    "tensor1 = torch.FloatTensor([\n",
    "    [1, 2, 3],\n",
    "    [4, 3, 1]\n",
    "])\n",
    "\n",
    "variable = Variable(tensor1, requires_grad=True)\n",
    "print(tensor1)\n",
    "print(variable)\n",
    "print(20*\"=\" + \"万能的分隔符\"+ 20*\"=\")\n",
    "print(\"经过Variable封装的数据和其余tensor进行运算，得到的结果还是variable形式\\n\",tensor1 + variable)\n",
    "print(20*\"=\" + \"万能的分隔符\"+ 20*\"=\")\n",
    "print('要想变成普通的tensor，要用.data方法：\\n',tensor1 + variable.data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <span id=6>6.torch.matmul()</span>\n",
    "## 矩阵的乘法\n",
    "* 0阶是一个标量\n",
    "* 1阶是一个向量\n",
    "* 2阶是一个矩阵\n",
    "> **torch.mul(a, b)** 矩阵的对位相乘，a，b维度必须相等\n",
    "> **torch.mm(a, b)** 矩阵的乘法，m * n  n* p  \n",
    ">> **mul** 点乘     **mm** 矩阵乘法  \n",
    ">> **matmul** 带有广播机制的乘法\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([10, 13,  6])\n",
      "torch.Size([3])\n",
      "====================万能的分隔符====================\n",
      "tensor([[10, 13,  6]])\n",
      "torch.Size([1, 3])\n",
      "====================万能的分隔符====================\n",
      "tensor([[10, 13,  6],\n",
      "        [16, 21, 10]])\n",
      "torch.Size([2, 3])\n"
     ]
    }
   ],
   "source": [
    "a = torch.tensor([1, 2])\n",
    "b = torch.tensor([[1, 2], [2, 3]])\n",
    "c = torch.tensor([\n",
    "    [2, 3, 2],\n",
    "    [4, 5, 2]\n",
    "])\n",
    "\n",
    "print(torch.matmul(a,c))\n",
    "print(torch.matmul(a,c).shape)\n",
    "print(20*\"=\" + \"万能的分隔符\" + 20*\"=\")\n",
    "print(torch.matmul(a.unsqueeze(0),c))\n",
    "print(torch.matmul(a.unsqueeze(0),c).shape)\n",
    "print(20*\"=\" + \"万能的分隔符\" + 20*\"=\")\n",
    "print(torch.matmul(b,c))\n",
    "print(torch.matmul(b,c).shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 7.np.triu()\n",
    "## 三角矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "k=-1:\n",
      " [[ 1  2  3]\n",
      " [ 4  5  6]\n",
      " [ 0  8  9]\n",
      " [ 0  0 12]]\n",
      "k = 0:\n",
      " [[1 2 3]\n",
      " [0 5 6]\n",
      " [0 0 9]\n",
      " [0 0 0]]\n",
      "k = 1:\n",
      " [[0 2 3]\n",
      " [0 0 6]\n",
      " [0 0 0]\n",
      " [0 0 0]]\n"
     ]
    }
   ],
   "source": [
    "a = np.array([\n",
    "    [1,2,3],\n",
    "    [4,5,6],\n",
    "    [7,8,9],\n",
    "    [10,11,12]\n",
    "])\n",
    "# k=-1不是对称阵的时候，向对角阵下方扩展一个对角线的数据\n",
    "print(\"k=-1:\\n\",np.triu(a, k=-1))\n",
    "# k=0不是对称阵的时候，形成对角矩阵\n",
    "print(\"k = 0:\\n\", np.triu(a, k=0))\n",
    "# k=1不是对称阵的时候，向对角阵上方收缩一个对角线的数据\n",
    "print(\"k = 1:\\n\", np.triu(a, k=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 8.太大的值对softmax的影响\n",
    "## 数量级对softmax得分的分布影响特别大，在数量较大时，softmax几乎将全部的概率分配给了最大值对应的标签\n",
    "\n",
    "$$Softmax(z_i) = \\frac{e^{z_i}}{\\sum_{c=1}^{C}e^{z_c}}$$  \n",
    "&emsp; &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;$z_i$&emsp;&emsp;&emsp;为第$i$个节点的输出值  \n",
    "&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;$C$为输出节点的个数，即分类的类别个数  \n",
    "&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;通过$Softmax$可以将多分类的输出转换为[0, 1]之间的概率\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "f = lambda x: exp(x * 2) / (exp(x) + exp(x) + exp(x * 2))\n",
    "x = np.linspace(0, 10, 100)\n",
    "y_3 = [f(x_i) for x_i in x]\n",
    "plt.plot(x, y_3)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 上图总结\n",
    "**[0, 10]分成100份传入softmax的结果**\n",
    "* 显然：从6~10得到的结果已经接近于1，所以值越大对softmax的结果影响越大"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <span id=9>9.logging</span>\n",
    "### 大量查看输出，记录想要的信息，相比于$print$能更好的控制输出在哪个地方，可以输出和控制消息级别过滤掉不需要的信息\n",
    "* $logging.basicConfig()$的参数：\n",
    "    * $level$: 输出日志的等级，$debug < info < warning  < error< critical$ **默认输出日志级别为warning**\n",
    "        * **debug** 10 详细信息，用于debug\n",
    "        * **info**  20 程序正常运行产生的一些信息\n",
    "        * **warning** 30 警告用户，虽然程序还在正常运行，但是可能发生了一些错误\n",
    "        * **error** 40 程序不能执行一些功能\n",
    "        * **critical** 50 程序不能继续运行\n",
    "    * $format$: 输出格式\n",
    "    * $datemat$: 时间信息\n",
    "    * $filename$: 日志信息输出到的日志文件名\n",
    "    * $filemode$: 决定使用什么模式来打开日志文件('r'、'w'、'a')，默认为'a'  \n",
    "***\n",
    "* $format$=$\\%(asctime)s$具体时间 $\\%(filename)s$文件名 $\\%(lenvelname)s$日志等级 $\\%(message)s$具体信息\n",
    "* $datemt$=$\\%a$星期 $\\%d$日期 $\\%b$月份 $\\%Y$年份 $\\%H:%M:%S$时间"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2021-Nov-26-13:03:31 <ipython-input-10-c33aa0f41d53> DEBUG name:zhang, age:0\n"
     ]
    }
   ],
   "source": [
    "# 使用basicConfig()指定日志输出的日志\n",
    "logging.basicConfig(\n",
    "    level=logging.DEBUG,\n",
    "    format='%(asctime)s %(filename)s %(levelname)s %(message)s',\n",
    "    datefmt='%Y-%b-%d-%H:%M:%S',\n",
    "    # filename='my.log',\n",
    "    # filemode='w'\n",
    ")\n",
    "\n",
    "name = 'zhang'\n",
    "age = '0'\n",
    "\n",
    "logging.debug(\"name:{}, age:{}\".format(name, age))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9.2logging的高级用法\n",
    "> * **Loggers**:记录器，提供应用程序代码能直接使用的接口\n",
    "> * **Handlers**:处理器，将记录器产生的日志发送至目的地；\n",
    "    * 可以将日志发送到文件、标准输出、邮件等\n",
    "        * StreamHandle、FileHandler\n",
    "> * **Filters**:过滤器，提供更好的粒度控制，决定哪些日志会被输出；\n",
    "> * **Formatters**:格式化器，设置日志内容的组成结构和消息字段。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 记录器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<RootLogger root (DEBUG)>\n",
      "<class 'logging.RootLogger'>\n"
     ]
    }
   ],
   "source": [
    "'''默认情况下是root日志，等级为warning'''\n",
    "logger = logging.getLogger()\n",
    "print(logger)\n",
    "print(type(logger))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<RootLogger root (DEBUG)>\n",
      "<class 'logging.RootLogger'>\n"
     ]
    }
   ],
   "source": [
    "# 修改等级为debug\n",
    "logger.setLevel(logging.DEBUG)\n",
    "print(logger)\n",
    "print(type(logger))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<Logger mylog (DEBUG)>\n",
      "<class 'logging.Logger'>\n"
     ]
    }
   ],
   "source": [
    "# 修改默认属性\n",
    "logger = logging.getLogger(\"mylog\")\n",
    "logger.setLevel(logging.DEBUG)\n",
    "print(logger)\n",
    "print(type(logger))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 处理器handler"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "consoleHandler = logging.StreamHandler()\n",
    "consoleHandler.setLevel(logging.DEBUG)\n",
    "\n",
    "# 没有给handler指定日志级别，将使用logger的级别，这里即是info\n",
    "fileHandler = logging.FileHandler(\"logger的高级用法.log\")\n",
    "fileHandler.setLevel(logging.DEBUG)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 格式formatter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "formatter = logging.Formatter('%(asctime)s %(filename)s %(levelname)s %(message)s')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 给处理器设置格式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "consoleHandler.setFormatter(formatter)\n",
    "fileHandler.setFormatter(formatter)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 记录器要设置处理器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "logger.addHandler(consoleHandler)\n",
    "# logger.addHandler(fileHandler)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2021-11-26 13:03:40,556 <ipython-input-18-f2127f75a558> DEBUG this is debug log\n",
      "2021-Nov-26-13:03:40 <ipython-input-18-f2127f75a558> DEBUG this is debug log\n",
      "2021-11-26 13:03:40,557 <ipython-input-18-f2127f75a558> INFO this is debug log\n",
      "2021-Nov-26-13:03:40 <ipython-input-18-f2127f75a558> INFO this is debug log\n",
      "2021-11-26 13:03:40,558 <ipython-input-18-f2127f75a558> WARNING this is debug log\n",
      "2021-Nov-26-13:03:40 <ipython-input-18-f2127f75a558> WARNING this is debug log\n",
      "2021-11-26 13:03:40,559 <ipython-input-18-f2127f75a558> ERROR this is debug log\n",
      "2021-Nov-26-13:03:40 <ipython-input-18-f2127f75a558> ERROR this is debug log\n"
     ]
    }
   ],
   "source": [
    "logger.debug(\"this is debug log\")\n",
    "logger.info(\"this is debug log\")\n",
    "logger.warning(\"this is debug log\")\n",
    "logger.error(\"this is debug log\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 实际应用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2021-11-26 14:27:26,828 [<ipython-input-2-19734027c6e8>:26][INFO] this is information\n",
      "2021-11-26 14:27:26,832 [<ipython-input-2-19734027c6e8>:27][WARNING] this is warning message\n",
      "2021-11-26 14:27:26,833 [<ipython-input-2-19734027c6e8>:28][ERROR] this is error message\n",
      "2021-11-26 14:27:26,833 [<ipython-input-2-19734027c6e8>:29][CRITICAL] this is critical message\n"
     ]
    }
   ],
   "source": [
    "def main():\n",
    "    # 获取logger实例，如果参数为空则返回root logger\n",
    "    logger = logging.getLogger(\"my_log\")\n",
    " \n",
    "    # 设置日志输出格式\n",
    "    \n",
    "    formatter = logging.Formatter('%(asctime)s [%(filename)s:%(lineno)s][%(levelname)s] %(message)s')\n",
    " \n",
    "    # 文件日志\n",
    "    file_handler = logging.FileHandler(\"test.log\")\n",
    "    file_handler.setFormatter(formatter)\n",
    " \n",
    "    # 控制台日志\n",
    "    console_handler = logging.StreamHandler(sys.stdout)\n",
    "    console_handler.setFormatter(formatter)\n",
    " \n",
    "    # 为logger添加的日志处理器\n",
    "    logger.addHandler(file_handler)\n",
    "    logger.addHandler(console_handler)\n",
    " \n",
    "    # 指定日志的最低输出级别，默认为WARN级别\n",
    "    logger.setLevel(logging.INFO)\n",
    " \n",
    "    # 输出不同级别的log\n",
    "    logger.debug('this is debug info')\n",
    "    logger.info('this is information')\n",
    "    logger.warning('this is warning message')\n",
    "    logger.error('this is error message')\n",
    "    logger.critical('this is critical message')\n",
    "    \n",
    "if  __name__ == '__main__':\n",
    "    main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
